/**
 * 
 */
package totalFileSize;

import java.io.File;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

/**
 * @author mingfan
 *
 */
public class TotalFileSizeWithBQueue {

	// Number of sub-directories that need to be explored in future
	private final AtomicLong pendingFileVisits = new AtomicLong(0);
	
	// Blocking queue shared by threads to put total file size of a given sub-directory
	// This number does not including file size of sub-directories of this sub-directory
	private final BlockingQueue<Long> partialFileSizes = new ArrayBlockingQueue<Long>(500);
	
	// Thread pool
	private ExecutorService threadPool = null;
	
	private void exploreOneDir(final File dir) {
		
		long totalSize = 0;
		if (dir.isFile()) {
			totalSize = dir.length();
		} else {
			final File[] subFiles = dir.listFiles();
			if (subFiles != null) {
				for (final File aSubFile : subFiles) {
					if (aSubFile.isFile()) {
						totalSize += aSubFile.length();
					} else {
						getTotalFileSizeInDir(aSubFile);
					}
				}
			}
		}
		try {
			partialFileSizes.put(totalSize);
		} catch (InterruptedException e) {
			throw new RuntimeException(e);
		}
		pendingFileVisits.decrementAndGet();
	}

	private void getTotalFileSizeInDir(final File dir) {
		pendingFileVisits.incrementAndGet();
		
		threadPool.execute(new Runnable() {

			@Override
			public void run() {
				exploreOneDir(dir);
			}
			
		});
	}
	
	private long getTotalFileSize(final int poolSize, final File dir) throws InterruptedException {
		threadPool = Executors.newFixedThreadPool(poolSize);
		try {
			getTotalFileSizeInDir(dir);
			long totalFileSize = 0;
			while (pendingFileVisits.get() > 0 || partialFileSizes.size() > 0) {
				totalFileSize += partialFileSizes.poll(10, TimeUnit.SECONDS);
			}
			return totalFileSize;
		} finally {
			threadPool.shutdown();
		}
	}
	
	public static void main(final String[] args ) throws InterruptedException {
		final String targetPath = "/lib";
		System.out.println("Start processing path: " + targetPath);
		final long start = System.nanoTime();
		final long totalSize = new TotalFileSizeWithBQueue().getTotalFileSize(8, new File(targetPath));
		final long end = System.nanoTime();
		System.out.printf("Total file size of path %s is %d.%n", targetPath, totalSize);
		System.out.println("Time used: "+ (end - start)/1.0e9);
	}
	
}
